#include <PlayState.h>

#include <PauseState.h>

template<> PlayState* Ogre::Singleton<PlayState>::ms_Singleton = 0;

void PlayState::enter()
{
  _root = Ogre::Root::getSingletonPtr();

  // Se recuperan el gestor de escena y la cámara.
  _sceneMgr = _root->getSceneManager("SceneManager");
  _camera = _sceneMgr->getCamera("MainCamera");
  _viewport = _root->getAutoCreatedWindow()->getViewport(0);
  _overlayMgr = Ogre::OverlayManager::getSingletonPtr();
  _overlayMgr->getByName("Screen")->show();
  _win = _root->getAutoCreatedWindow();

  _endGame = false;
  _exitGame = false;
  _shipDirL = false;
  _shipDirR = false;
  _shipShot = false;
  _counter = 0;
  _score = 0;

  Ogre::OverlayElement* oe = _overlayMgr->getOverlayElement("puntos");
  oe->setLeft(655 * _win->getWidth() / 1280);
  oe->setTop(20 * _win->getHeight() / 800);

  startGame();
}

void PlayState::exit()
{
  _overlayMgr->getByName("Screen")->hide();
  _sceneMgr->clearScene();

  if (_score == 0) return;

  std::list<unsigned int>* records = IntroState::getSingletonPtr()->getRecords();
  records->push_back(_score);
  records->unique();
  records->sort();
  records->reverse();

  while (records->size() > 10)
    records->pop_back();
}

void PlayState::pause()
{
  _shipDirL = false;
  _shipDirR = false;
}

void PlayState::resume() {}

bool PlayState::frameStarted(const Ogre::FrameEvent& evt)
{
  if (_endGame)
    popState();

  else
  {
    _overlayMgr->getOverlayElement("puntos")->setCaption(
        Ogre::StringConverter::toString(_score));

    Ogre::Real deltaT = evt.timeSinceLastFrame;

    // Mover defensor
    moveDefender(deltaT);

    // Mover invasores
    moveInvaders(deltaT);

    // Disparo defensor
    shotDefender(deltaT);

    // Disparo invasores
    shotInvaders(deltaT);
  }

  return true;
}

bool PlayState::frameEnded(const Ogre::FrameEvent& evt)
{
  return not _exitGame;
}

void PlayState::keyPressed(const OIS::KeyEvent& e)
{
  // Tecla p --> PauseState.
  if (e.key == OIS::KC_P)
    pushState(PauseState::getSingletonPtr());

  if (e.key == OIS::KC_LEFT) _shipDirL = true;
  if (e.key == OIS::KC_RIGHT) _shipDirR = true;

  if (e.key == OIS::KC_SPACE) _shipShot = true;

  if (e.key == OIS::KC_ESCAPE)
    popState();

}

void PlayState::keyReleased(const OIS::KeyEvent& e)
{
  if (e.key == OIS::KC_LEFT) _shipDirL = false;
  if (e.key == OIS::KC_RIGHT) _shipDirR = false;
}

void PlayState::mouseMoved(const OIS::MouseEvent& e) {}

void PlayState::mousePressed(const OIS::MouseEvent& e, OIS::MouseButtonID id) {}

void PlayState::mouseReleased(const OIS::MouseEvent& e, OIS::MouseButtonID id) {}

PlayState* PlayState::getSingletonPtr()
{
  return ms_Singleton;
}

PlayState& PlayState::getSingleton()
{ 
  assert(ms_Singleton);
  return *ms_Singleton;
}

void PlayState::startGame()
{
  std::ostringstream shipStr, shipNodeStr, shipMeshStr;
  int i, j;

  // Varibles del juego
  _turn = 0.5;
  _cont = 20;
  _dir = left;
  _defShot = NULL;
  _invShot = NULL;
  
  // Plano del espacio
  Ogre::Plane groundPlane(Ogre::Vector3::UNIT_Z, -10);
  Ogre::MeshManager::getSingleton().createPlane("groundPlane",
	Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, groundPlane,
	80,128,1,1,true,1,1,1,Ogre::Vector3::UNIT_X);

  Ogre::SceneNode* groundNode = _sceneMgr->createSceneNode("groundNode");
  Ogre::Entity* groundEnt = _sceneMgr->createEntity("planeEnt", "groundPlane");
  groundEnt->setMaterialName("Space");

  groundNode->attachObject(groundEnt);

  // Luces
  _sceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_MODULATIVE);
  Ogre::Light* light = _sceneMgr->createLight("light");
  light->setType(Ogre::Light::LT_POINT);
  light->setPosition(Ogre::Vector3(0,0,100));
  groundNode->attachObject(light);

  _sceneMgr->getRootSceneNode()->addChild(groundNode);  

  // Defensor

  // Entity
  Ogre::Entity *defEnt = _sceneMgr->createEntity("Defender", "MeshNave.mesh");

  // AnimationState
  Ogre::AnimationState *defAnim = defEnt->getAnimationState("Girar");
  defAnim->setEnabled(true);
  defAnim->setLoop(true);
  defAnim->setTimePosition(0.0);

  // Node
  _defender = _sceneMgr->createSceneNode("Defender");
  _defender->attachObject(defEnt);
  _defender->translate(0, -25, 0);
  _sceneMgr->getRootSceneNode()->addChild(_defender);

  // Invasores
  srand((unsigned)time(NULL));
  _invaders = _sceneMgr->createSceneNode("Invaders");

  // Filas invasoras
  for (i = 0; i < 5; i++)
  {
    switch (i)
    {
      case 0:	case 1:	shipStr << "Taladro"; break;
      case 2: case 3:	shipStr << "Martillo"; break;
      case 4: shipStr << "Sierra"; break;
    }
    shipMeshStr << "Mesh" << shipStr.str() << ".mesh";

    // Columnas invasoras
    for (j = 0; j < 11; j++) {
      shipNodeStr << shipStr.str() << j << "_" << i;

      // Entity
      Ogre::Entity *shipEnt = _sceneMgr->createEntity(shipNodeStr.str(), 
          shipMeshStr.str());
      // AnimationState
      Ogre::AnimationState *shipAnim = 
        shipEnt->getAnimationState(shipStr.str());
      shipAnim->setEnabled(true);
      shipAnim->setLoop(true);
      shipAnim->setTimePosition(rand() % 10);

      // SceneNode
      Ogre::SceneNode *shipNode = 
        _sceneMgr->createSceneNode(shipNodeStr.str());
      shipNode->attachObject(shipEnt);
      shipNode->translate(-25 + j * 5, -10 + i * 5, 0);
      _invaders->addChild(shipNode);

      shipNodeStr.str("");
      ++_counter;
    }

    shipStr.str("");
    shipMeshStr.str("");
  }

  _invaders->translate(-10, 15, 0);  
  _sceneMgr->getRootSceneNode()->addChild(_invaders);
}

void PlayState::moveDefender(const Ogre::Real deltaT)
{
  Ogre::Entity *defEnt;
  Ogre::Vector3 vt(0, 0, 0);

  // Movimiento nave
  _turn = _turn > 0 ? 0.5 : -0.5;

  if (_shipDirL)
  {
    if (_defender->getPosition().x > -35) vt += Ogre::Vector3(-1, 0, 0);
    _turn = -1;
  }

  if (_shipDirR)
  {
    if (_defender->getPosition().x < 35) vt += Ogre::Vector3(1, 0, 0);
    _turn = 1;
  }

  _defender->translate(vt * deltaT * 20);
  defEnt = static_cast<Ogre::Entity *>(_defender->getAttachedObject(0));
  defEnt->getAnimationState("Girar")->addTime(_turn * deltaT);

  // Disparo nave
  if (_shipShot)
  {
    if (_defShot == NULL)
    {
      Ogre::Entity *shotEnt = _sceneMgr->createEntity("defShot", "MeshShot.mesh");
      _defShot = _sceneMgr->createSceneNode("defShot");
      _defShot->translate(_defender->getPosition());
      _defShot->attachObject(shotEnt);
      _sceneMgr->getRootSceneNode()->addChild(_defShot);
    }
    _shipShot = false;
  }
}


void PlayState::moveInvaders(const Ogre::Real deltaT)
{
  Ogre::Vector3 vt(0, 0, 0);

  // Mover invasores
  switch (_dir)
  {
    case left:
      vt += Ogre::Vector3(deltaT * 4, 0, 0);
      _cont -= deltaT * 4;
      if (_cont <= 0) { _cont = 5; _dir = downright; }
      break;

    case downright:
      vt += Ogre::Vector3(0, -deltaT * 4, 0);
      _cont -= deltaT * 4;
      if (_cont <= 0) { _cont = 20; _dir = right; }
      break;

    case right:
      vt += Ogre::Vector3(-deltaT * 4, 0, 0);
      _cont -= deltaT * 4;
      if (_cont <= 0) { _cont = 5; _dir = downleft; }
      break;

    case downleft:
      vt += Ogre::Vector3(0, -deltaT * 4, 0);
      _cont -= deltaT * 4;
      if (_cont <= 0) { _cont = 20; _dir = left; }
      break;
  }
  _invaders->translate(vt);

	// Invasor alcanza defensor, fin del juego
  Ogre::AxisAlignedBox boundingBox;
  boundingBox = _invaders->_getWorldAABB();
  
  if (boundingBox.getMinimum().y <= _defender->getPosition().y)
  	    gameOver(Fail);

  // Animar naves y disparar
  Ogre::Entity *shipEnt;
  Ogre::SceneNode::ChildNodeIterator it = _invaders->getChildIterator();
  Ogre::AnimationState *shipAnim;
  Ogre::SceneNode *shipNode;

  while (it.hasMoreElements())
  {
    shipNode = static_cast<Ogre::SceneNode *>(it.getNext());

    shipEnt = static_cast<Ogre::Entity *>(shipNode->getAttachedObject(0));

    if (shipEnt->getName()[0] == 'T') shipAnim = shipEnt->getAnimationState("Taladro");
    if (shipEnt->getName()[0] == 'M') shipAnim = shipEnt->getAnimationState("Martillo");
    if (shipEnt->getName()[0] == 'S') shipAnim = shipEnt->getAnimationState("Sierra");

    shipAnim->addTime(deltaT / 2);

    if (rand() % 55 == 0 && _invShot == NULL)
    {
      Ogre::Entity *shotEnt = _sceneMgr->createEntity("invShot", "MeshShot.mesh");
      shotEnt->setMaterialName("InvShotMaterial");
      _invShot = _sceneMgr->createSceneNode("invShot");
      _invShot->translate(shipNode->_getDerivedPosition());
      _invShot->attachObject(shotEnt);
      _sceneMgr->getRootSceneNode()->addChild(_invShot);
    }
  }
}

void PlayState::shotDefender(const Ogre::Real deltaT)
{
  if (_defShot == NULL) return;

  if (_defShot->getPosition().y > 30)
  {
    _sceneMgr->destroySceneNode("defShot");
    _sceneMgr->destroyEntity("defShot");
    _defShot = NULL;
    return;
  }

  _defShot->translate(0, deltaT * 60, 0);

  // Detectar colision
  Ogre::SceneNode::ChildNodeIterator it = _invaders->getChildIterator();
  Ogre::SceneNode *shipNode;
  Ogre::AxisAlignedBox boundingBox;

  while (it.hasMoreElements())
  {
    shipNode = static_cast<Ogre::SceneNode*>(it.getNext());

    boundingBox = shipNode->_getWorldAABB();

    if (boundingBox.contains(_defShot->getPosition()))
    {
			_sceneMgr->destroyEntity(shipNode->getName());
			_invaders->removeChild(shipNode);
			_sceneMgr->getRootSceneNode()->addChild(shipNode);
			shipNode->setPosition(boundingBox.getCenter());
		  Ogre::ParticleSystem* ps = _sceneMgr->createParticleSystem(shipNode->getName(), "explosion");
			shipNode->attachObject(ps);
			
      _sceneMgr->destroySceneNode("defShot");
      _sceneMgr->destroyEntity("defShot");
      _defShot = NULL;
      _score += 10;

      if (--_counter == 0) gameOver(Win);
      break;
    }		
  }
}

void PlayState::shotInvaders(const Ogre::Real deltaT)
{
  if (_invShot == NULL) return;

  if (_invShot->getPosition().y < -30)
  {
    _sceneMgr->destroySceneNode("invShot");
    _sceneMgr->destroyEntity("invShot");
    _invShot = NULL;
    return;
  }

  _invShot->translate(0, -deltaT * 30, 0);

  // Detectar colision
  Ogre::AxisAlignedBox boundingBox;

  boundingBox = _defender->_getWorldAABB();

  if (boundingBox.contains(_invShot->getPosition()))
  {
    _sceneMgr->destroySceneNode("invShot");
    _sceneMgr->destroyEntity("invShot");
    _invShot = NULL;
    gameOver(Fail);
  }		
}

void PlayState::gameOver(const EndType t)
{
  EndGameState* e = EndGameState::getSingletonPtr();
  e->setType(t);
  pushState(e);
  _endGame = true;
}
